﻿/*
 * RegExpress
 * 
 * Copyright (c) 2010, Daniel McGaughran
 * 
 * Licensed under the Apache Licence, Version 2.0 (the "Licence");
 * you may not use this file except in compliance with the Licence.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 * 
 */

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using XrControls.Entities;


namespace XrControls.Util
{
    public class KeyCommands : DependencyObject
    {
        public static readonly DependencyProperty SetNameProperty;
    	private static Dictionary<string, KeyCommandSet> _keyCommandSets;


		static KeyCommands()
        {
			_keyCommandSets = new Dictionary<string, KeyCommandSet>();
			
			SetNameProperty = DependencyProperty.RegisterAttached("SetName", typeof(string), typeof(KeyCommands),
                new FrameworkPropertyMetadata("", AssignSet));
        }



		public static KeyCommandSet GetCommandSet(string setName)
		{
			if(_keyCommandSets.ContainsKey(setName))
				return _keyCommandSets[setName];

			return null;
		}



		public static void AddCommandSet(string setName)
		{
			_keyCommandSets[setName] = new KeyCommandSet();
		}


		public static void AddCommandSet(string setName, KeyCommandSet commandSet)
		{
			_keyCommandSets[setName] = commandSet;
		}



		public static void RemoveCommandSet(string setName)
		{
			if(_keyCommandSets.ContainsKey(setName))
				_keyCommandSets.Remove(setName);
		}



    	public static string GetSetName(FrameworkElement target)
        {
            return (string)target.GetValue(SetNameProperty);
        }

		public static void SetSetName(FrameworkElement target, string value)
        {
            target.SetValue(SetNameProperty, value);
        }



        static void AssignSet(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            if (target is FrameworkElement)
            {
                FrameworkElement castTarget = (FrameworkElement)target;

                if(!String.IsNullOrEmpty((string)e.NewValue))
					castTarget.KeyDown += InvokeCommandFromKeypress;
                else
					castTarget.KeyDown -= InvokeCommandFromKeypress;
            }
        }



        static void InvokeCommandFromKeypress(object sender, KeyEventArgs e)
        {
        	FrameworkElement sourceElement = (FrameworkElement)(sender);
        	string setName = GetSetName(sourceElement);

			if(String.IsNullOrEmpty(setName) || IsModifier(e.Key))
				return;

			//Get command set
        	KeyCommandSet commandSet = GetCommandSet(setName);
			if(commandSet == null) return;

			//Get key expression
        	ModifierKeys modifiers = e.KeyboardDevice.Modifiers;
        	string modifierPrefix = "";
			if(modifiers != ModifierKeys.None)
			{
				string ctrl = ((modifiers & ModifierKeys.Control) != ModifierKeys.None) ? "C" : "";
				string alt = ( ( modifiers & ModifierKeys.Alt ) != ModifierKeys.None ) ? "A" : "";
				string shift = ( ( modifiers & ModifierKeys.Shift ) != ModifierKeys.None ) ? "S" : "";
				modifierPrefix = String.Format("{0}{1}{2}+", ctrl, alt, shift);
			}

        	string keyExpression = String.Format("{0}{1}", modifierPrefix, e.Key);
        	ICommand command = commandSet[keyExpression];

			if(command != null)
			{
				//Trap this keystroke so that it doesn't invoke any higher-level commands,
				// even if the command cannot be executed.
				e.Handled = true; 

				if(command.CanExecute(null))
					command.Execute(null);
			}
        }



		private static bool IsModifier(Key key)
		{
			return key == Key.LeftAlt || key == Key.LeftCtrl || key == Key.LeftShift
				|| key == Key.RightAlt || key == Key.RightCtrl || key == Key.RightShift;
		}


    }
}
